package com.lin.filegraph.utils.parser.astvisitor;

import com.lin.filegraph.utils.parser.simpleast.SimpleASTHelper;
import com.lin.filegraph.utils.parser.node.JMethodInvocationNode;
import com.lin.filegraph.utils.parser.node.JMethodNode;
import com.lin.filegraph.utils.parser.node.JTypeNode;
import org.eclipse.jdt.core.dom.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @description:
 * @author: linhuaixu
 * @time: 2023/5/18 14:15
 */

public class MethodInvocationVisitor extends ASTVisitor {

    private JMethodNode methodNode = null;

    public MethodInvocationVisitor(JMethodNode methodNode) {
        this.methodNode = methodNode;
    }

    public boolean visit(MethodInvocation node) {
        if (node.resolveMethodBinding() != null) {
            IMethodBinding iMethodBinding = node.resolveMethodBinding();
            String strIvkMtdName = null; // �����õķ�����
            String strReturnType = null; // ���÷�����������
            JMethodNode outerMethod = methodNode;// �����������ڵ��ⲿ����
            String strOuterType = methodNode.getFatherType().getTypeName();// �����������ڵ���
            JMethodNode declaringMethod = null;// �÷����Ķ��崦
            String strDeclaringType = null;// ���õķ�����������
            List<String> listActualParaTypes = new ArrayList<String>();// ���÷�����ʵ�������б�
            strIvkMtdName = node.getName().toString();
            strReturnType = iMethodBinding.getReturnType().getQualifiedName();
            // ���õķ�����������
            strDeclaringType = iMethodBinding.getDeclaringClass().getQualifiedName();
            String strDeclaringMethodSignature = iMethodBinding.toString();
            JTypeNode declaringType = SimpleASTHelper.getTypeByName(strDeclaringType, methodNode.getRoot());
            if (declaringType != null) {
                List<JMethodNode> listOfCandidateDeclaringMethod = declaringType.getAllMethods();
                for (JMethodNode candidateDeclaringMethod : listOfCandidateDeclaringMethod) {
                    if (candidateDeclaringMethod.getMethodSignature().equals(strDeclaringMethodSignature)) {
                        declaringMethod = candidateDeclaringMethod;
                        break;
                    }
                }
            }
            if(declaringMethod == null) {
                return true;
            }
            List paramters = node.arguments();
            for (Object obj : paramters) {
                if (obj instanceof Expression) {
                    Expression parameter = (Expression) obj;
                    if (parameter.resolveTypeBinding() != null) {
                        String param = parameter.resolveTypeBinding().getQualifiedName();
                        listActualParaTypes.add(param);
                    }
                }
            }
            JMethodInvocationNode methodInvocationNode = new JMethodInvocationNode(methodNode.getFilePath(), node,
                    strIvkMtdName, strReturnType, strOuterType, outerMethod, declaringMethod, strDeclaringType,
                    listActualParaTypes);
            methodNode.addMethodInvocationNode(methodInvocationNode);
        }
        return true;
    }

    public boolean visit(ClassInstanceCreation node) {
        if (node.resolveConstructorBinding() != null) {
            IMethodBinding iMethodBinding = node.resolveConstructorBinding();
            String strIvkMtdName = null; // �����õķ�����
            String strReturnType = null; // ���÷�����������
            JMethodNode outerMethod = methodNode;// �����������ڵ��ⲿ����
            String strOuterType = methodNode.getFatherType().getTypeName();// �����������ڵ���
            JMethodNode declaringMethod = null;// �÷����Ķ��崦
            String strDeclaringType = null;// ���õķ�����������
            List<String> listActualParaTypes = new ArrayList<String>();// ���÷�����ʵ�������б�
            strIvkMtdName = iMethodBinding.getName();
            strReturnType = iMethodBinding.getReturnType().getQualifiedName();
            // ���õķ�����������
            strDeclaringType = iMethodBinding.getDeclaringClass().getQualifiedName();
            String strDeclaringMethodSignature = iMethodBinding.toString();

            JTypeNode declaringType = SimpleASTHelper.getTypeByName(strDeclaringType, methodNode.getRoot());
            if (declaringType != null) {
                List<JMethodNode> listOfCandidateDeclaringMethod = declaringType.getAllMethods();
                for (JMethodNode candidateDeclaringMethod : listOfCandidateDeclaringMethod) {
                    if (candidateDeclaringMethod.getMethodSignature().equals(strDeclaringMethodSignature)) {
                        declaringMethod = candidateDeclaringMethod;
                        break;
                    }
                }
            }
            if(declaringMethod == null) {
                return true;
            }
            List paramters = node.arguments();
            for (Object obj : paramters) {
                if (obj instanceof Expression) {
                    Expression parameter = (Expression) obj;
                    if (parameter.resolveTypeBinding() != null) {
                        String param = parameter.resolveTypeBinding().getQualifiedName();
                        listActualParaTypes.add(param);
                    }
                }
            }

            JMethodInvocationNode methodInvocationNode = new JMethodInvocationNode(methodNode.getFilePath(), node,
                    strIvkMtdName, strReturnType, strOuterType, outerMethod, declaringMethod, strDeclaringType,
                    listActualParaTypes);
            methodNode.addMethodInvocationNode(methodInvocationNode);
        }
        return true;
    }

    // @Override
    // public boolean visit(ConstructorInvocation node) {
    // System.out.println("ConstructorInvocation");
    // return true;
    //
    // }
    @Override
    public boolean visit(SuperConstructorInvocation node) {
        if (node.resolveConstructorBinding() != null) {
            IMethodBinding iMethodBinding = node.resolveConstructorBinding();
            String strIvkMtdName = null; // �����õķ�����
            String strReturnType = null; // ���÷�����������
            JMethodNode outerMethod = methodNode;// �����������ڵ��ⲿ����
            String strOuterType = methodNode.getFatherType().getTypeName();// �����������ڵ���
            JMethodNode declaringMethod = null;// �÷����Ķ��崦
            String strDeclaringType = null;// ���õķ�����������
            List<String> listActualParaTypes = new ArrayList<String>();// ���÷�����ʵ�������б�
            strIvkMtdName = iMethodBinding.getName();
            strReturnType = iMethodBinding.getReturnType().getQualifiedName();
            // ���õķ�����������
            strDeclaringType = iMethodBinding.getDeclaringClass().getQualifiedName();
            String strDeclaringMethodSignature = iMethodBinding.toString();
            JTypeNode declaringType = SimpleASTHelper.getTypeByName(strDeclaringType, methodNode.getRoot());
            if (declaringType != null) {
                List<JMethodNode> listOfCandidateDeclaringMethod = declaringType.getAllMethods();
                for (JMethodNode candidateDeclaringMethod : listOfCandidateDeclaringMethod) {
                    if (candidateDeclaringMethod.getMethodSignature().equals(strDeclaringMethodSignature)) {
                        declaringMethod = candidateDeclaringMethod;
                        break;
                    }
                }
            }
            if(declaringMethod == null) {
                return true;
            }
            List paramters = node.arguments();
            for (Object obj : paramters) {
                if (obj instanceof Expression) {
                    Expression parameter = (Expression) obj;
                    if (parameter.resolveTypeBinding() != null) {
                        String param = parameter.resolveTypeBinding().getQualifiedName();
                        listActualParaTypes.add(param);
                    }
                }
            }
            JMethodInvocationNode methodInvocationNode = new JMethodInvocationNode(methodNode.getFilePath(), node,
                    strIvkMtdName, strReturnType, strOuterType, outerMethod, declaringMethod, strDeclaringType,
                    listActualParaTypes);
            methodNode.addMethodInvocationNode(methodInvocationNode);
        }
        return true;
    }

    @Override
    public boolean visit(TypeDeclaration node) {
        return false;
    }

    @Override
    public boolean visit(EnumDeclaration node) {
        return false;
    }

    @Override
    public boolean visit(AnonymousClassDeclaration node) {
        return false;
    }

    public void traverse() {
        methodNode.getAstNode().accept(this);
    }
}

